{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Aliases"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Although member values are considered unique in enumerations, we can still define multiple member names with the same value. But they do not create different members!\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "They are, in fact, considered aliases of each other, with the first member becoming the \"master\" member."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see a simple example of this first:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import enum"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class NumSides(enum.Enum):\n",
    "    Triangle = 3\n",
    "    Rectangle = 4\n",
    "    Square = 4\n",
    "    Rhombus = 4"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see we have two members with different names (names must **always** be unique), but with the **same** value."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However, the `Square` and `Rhombus` members are considered **aliases** of the `Rectangle` member since `Rectangle` is defined first."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This means that `Rectangle` and `Square` are actually considered the **same** member:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "NumSides.Rectangle is NumSides.Square"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And of course aliases are equal to each other too:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "NumSides.Square is NumSides.Rhombus"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aliases can be referenced just like an ordinary member, and are considered *contained* in the enumeration:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "NumSides.Square in NumSides"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And when we look up the member, by value:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<NumSides.Rectangle: 4>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "NumSides(4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "we always get the \"master\" back."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Same holds when when looking up by key:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<NumSides.Rectangle: 4>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "NumSides['Square']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When we iterate an enumeration that contains aliases, none of the aliases are returned in the iteration:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<NumSides.Triangle: 3>, <NumSides.Rectangle: 4>]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(NumSides)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The only way to get all the members, including aliases, is to use the `__members__` property:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "mappingproxy({'Triangle': <NumSides.Triangle: 3>,\n",
       "              'Rectangle': <NumSides.Rectangle: 4>,\n",
       "              'Square': <NumSides.Rectangle: 4>,\n",
       "              'Rhombus': <NumSides.Rectangle: 4>})"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "NumSides.__members__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice how the aliases are treated. Although the keys in the mapping proxy are different, the object they point to are all the \"master\" member."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Example"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are times when the ability to define these aliases can be useful. Let's say you have to deal with statuses that are returned as strings from different systems."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "These systems may not always define exactly the same strings to mean the same thing (maybe they were developed independently). In a case like this, being able to create aliases could be useful to bring uniformity to our own code."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's say that the statuses from system 1 are: `ready, busy, finished_no_error, finished_with_errors`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And for system 2 we have correspondingly: `ready, processing, ran_ok, errored`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And in our own system we might want the statuses: `ready, running, ok, errors`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In other words we have:\n",
    "\n",
    "```\n",
    "Us        System 1               System 2\n",
    "-------------------------------------------\n",
    "ready     ready                  ready\n",
    "running   busy                   processing\n",
    "ok        finished_no_error      ran_ok\n",
    "errors    finished_with_errors   errored\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can the easily achieve this using this class with aliases:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Status(enum.Enum):\n",
    "    ready = 'ready'\n",
    "    \n",
    "    running = 'running'\n",
    "    busy = 'running'\n",
    "    processing = 'running'\n",
    "    \n",
    "    ok = 'ok'\n",
    "    finished_no_error = 'ok'\n",
    "    ran_ok = 'ok'\n",
    "    \n",
    "    errors = 'errors'\n",
    "    finished_with_errors = 'errors'\n",
    "    errored = 'errors'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then when we list our own statuses, we only see our (master) members:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<Status.ready: 'ready'>,\n",
       " <Status.running: 'running'>,\n",
       " <Status.ok: 'ok'>,\n",
       " <Status.errors: 'errors'>]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(Status)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But now we can look up a status from any of the other two systems, and automatically get our \"master\" member:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Status.running: 'running'>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Status['busy']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Status.running: 'running'>"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Status['processing']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that in our case the actual value of the members does not matter. I used strings, but we could equally well just use numbers:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Status(enum.Enum):\n",
    "    ready = 1\n",
    "    \n",
    "    running = 2\n",
    "    busy = 2\n",
    "    processing = 2\n",
    "    \n",
    "    ok = 3\n",
    "    finished_no_error = 3\n",
    "    ran_ok = 3\n",
    "    \n",
    "    errors = 4\n",
    "    finished_with_errors = 4\n",
    "    errored = 4"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This will work the same way:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Status.ok: 3>"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Status.ran_ok"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "status = 'ran_ok'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "status in Status.__members__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Status.ok: 3>"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Status[status]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Ensuring No Aliases"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sometimes we want to make sure we are creating enumerations that do **not** contain aliases."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Of course, we can just be careful and not define aliases, but the `enum` module provides a special decorator that can enforce this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "@enum.unique\n",
    "class Status(enum.Enum):\n",
    "    ready = 1\n",
    "    done_ok = 2\n",
    "    errors = 3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And if we try to create aliases, our code will not compile - we'll get an exception as soon as the class is compiled:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "duplicate values found in <enum 'Status'>: waiting -> ready\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    @enum.unique\n",
    "    class Status(enum.Enum):\n",
    "        ready = 1\n",
    "        waiting = 1\n",
    "        done_ok = 2\n",
    "        errors = 3\n",
    "except ValueError as ex:\n",
    "    print(ex)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So if you know that your enumeration should never contain aliases, go ahead and use the decorator for extra safety."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
